using System;
using System.Drawing;
using System.ComponentModel;
using System.Threading;
using System.Web;
using System.Web.UI;
using System.IO;

namespace Library.WebControls.SimpleCallback {
	/// <summary>
	/// Class that provide the Callback functionality
	/// </summary>
	[ToolboxBitmap(typeof(SpCallback), "Library.WebControls.SimpleCallback.SpCallback.bmp")]
	[Designer(typeof(SpCallbackDesign))]
	[SupportsEventValidation]
	[ToolboxData("<{0}:SpCallback runat=server></{0}:SpCallback>")]
	[DesignTimeVisible(true)]
	public class SpCallback : BaseAjaxControl {
		private bool _isCallback;
		private static readonly Type CallBackType = typeof(SpCallback);
		private static readonly object CustomCallbackEvent = new object();
		private static readonly object CustomDownloadEvent = new object();
		private const string CustomCallbackType = "1";
		private const string CusttomDownloadType = "2";

		protected const string SpCallbackControlCallbackParameter = "__SPCALLBACKRQP_";
		protected const string SpCallbackControlCallbackData = "__SPCALLBACKRQDATA_";
		protected const string SpCallbackControlServerId = "__SPCALLBACKRQSID_";
		protected const string SpCallbackControlCallbackType = "__SPCALLBACKRQTYPE_";
		protected const string SoCallbackControlCalbackDownloadToken = "__SPCALLBACKRQDLT_";
		/// <summary>
		/// Determine request from the callback control
		/// </summary>
		/// <param name="page"></param>
		/// <returns></returns>
		public static bool IsSpCallbackCallback(Page page) {
			try {
				return page.Request[SpCallbackControlCallbackParameter] == "T";
			} catch {
				return false;
			}
		}


		/// <summary>
		/// client handle for call back complete
		/// </summary>
		//	public string CallbackCompleteFunction { get; set; }

		/// <summary>
		/// determine whater user perform data for callback or CallbackControl perform data for callback
		/// </summary>
		public bool UserPerformDataCallback { get; set; }

		/// <summary>
		/// Indicates whether the request is callbacked by callback control
		/// </summary>
		public override bool IsCallback {
			get {
				return _isCallback;
			}
		}

		private string _callbackType;
		protected override void OnInit(EventArgs e) {
			base.OnInit(e);
			if (Request != null) {
				_isCallback = IsSpCallbackCallback(Page) && ClientID == Request[SpCallbackControlServerId];
				if (_isCallback) {
					_callbackType = Request[SpCallbackControlCallbackType];
				}
			} else {
				_isCallback = false;
			}
		}

		protected override void OnLoad(EventArgs e) {
			base.OnLoad(e);
			if (IsCallback) {
				switch (_callbackType) {
					case CustomCallbackType:
						OnCustomCallback(new CustomCallbackEventArgs(Request[SpCallbackControlCallbackData]));
						break;
					case CusttomDownloadType:
						if (AsyncDownload) {
							if (Page != null) {
								Page.AddOnPreRenderCompleteAsync(BeginAsyncOperation, EndAsyncOperation);
							}
						} else {
							OnCustomDownload(new CustomDownloadEventArgs(Request[SpCallbackControlCallbackData]));
						}
						break;
				}

			}
			IncludeScriptOnLoad(CallBackType, "CB", "Library.WebControls.SimpleCallback.include.SpCallback.js", UtilLib);
		}
		/// <summary>
		/// Get or Set download is asyn
		/// </summary>
		public bool AsyncDownload { get; set; }
		IAsyncResult BeginAsyncOperation(object sender, EventArgs e, AsyncCallback cb, object state) {

			var t = new ThreadStart((AnsynHandleDownload));
			return t.BeginInvoke(cb, state);
		}

		private CustomDownloadEventArgs _customDownloadEventArgs;
		void EndAsyncOperation(IAsyncResult ar) {
			ProcessDownload(_customDownloadEventArgs);
		}



		private void AnsynHandleDownload() {
			_customDownloadEventArgs = new CustomDownloadEventArgs(Request[SpCallbackControlCallbackData]);
			OnCustomDownload(_customDownloadEventArgs);
		}


		protected override void CreateClientObjectAtRender(HtmlTextWriter writer) {
			BeginScriptTag(writer);
			//writer.Write(String.Format("var {0} = new SpCallback();", ClientName));
			writer.Write(String.Format("var {0} = new SpCallback('{0}', '{1}', {2});", ClientName, ClientID, UserPerformDataCallback.ToString().ToLower()));
			EndScriptTag(writer);
		}

		protected override void Render(HtmlTextWriter writer) {
			if (Visible) {
				if (IsCallback && _callbackType == CustomCallbackType) {
					WriteCustomCallbackCompleteResult();
					FlushAndEndCallback();
				} else {
					base.Render(writer);
				}
			}
		}
		/// <summary>
		/// Process download
		/// </summary>
		/// <param name="e"></param>
		public void ProcessDownload(CustomDownloadEventArgs e) {
			try {
				DownloadNow(e);
			} catch (Exception ex) {
				DownLoadError(ex.ToString());
			}
		}
		private void DownloadNow(CustomDownloadEventArgs e) {


			if (String.IsNullOrEmpty(e.FileName)) {
				DownLoadError("File name is null");
				return;
			}
			if (e.Error) {
				DownLoadError(e.ErrorText);
				return;
			}

			Response.Clear();
			Response.ClearHeaders();
			Response.ClearContent();

			if (!String.IsNullOrEmpty(e.ContentType)) {
				Response.ContentType = e.ContentType;
			}
			//Response.AddHeader("encoding", "UTF-8");
			Response.AddHeader("content-disposition", "attachment; filename=" + Path.GetFileName(e.FileName));
			Response.AppendCookie(new HttpCookie("spDownloadControlTokenKey", ConvertToArrayString(Request[SoCallbackControlCalbackDownloadToken], e.Result)));
			const int bufferLength = 10000;
			//
			int length = 0;
			Stream download = null;
			try {
				if (e.FileContent == null || e.FileName.Length == 0) {
					download = new FileStream(e.FileName, FileMode.Open, FileAccess.Read);
					Response.AddHeader("Content-Length", download.Length.ToString());
					var buffer = new Byte[bufferLength];
					do {
						if (Response.IsClientConnected) {
							length = download.Read(buffer, 0, bufferLength);
							Response.OutputStream.Write(buffer, 0, length);
							buffer = new Byte[bufferLength];
						} else {
							length = -1;
						}
					} while (length > 0);
				} else {
					Response.AddHeader("Content-Length", e.FileContent.Length.ToString());
					int count;
					do {
						if (Response.IsClientConnected) {
							count = e.FileContent.Length - length;
							if (count > bufferLength) count = bufferLength;
							else if (count <= 0) break;
							Response.OutputStream.Write(e.FileContent, length, count);
							length += count;
						} else {
							count = -1;
						}
					} while (count > 0);
				}

				Response.Flush();
				Response.End();
			} finally {
				if (download != null)
					download.Close();
			}
			if (e.DeleteFile) {
				try {
					File.Delete(e.FileName);
					// ReSharper disable EmptyGeneralCatchClause
				} catch {
					// ReSharper restore EmptyGeneralCatchClause
				}
			}
		}

		protected virtual void OnCustomDownload(CustomDownloadEventArgs e) {
			var handler = (EventHandler<CustomDownloadEventArgs>)Events[CustomDownloadEvent];
			if (handler != null) {
				try {
					handler(this, e);
					//if (!e.DownloadLater)
					DownloadNow(e);
				} catch (Exception ex) {
					DownLoadError(ex.ToString());
				}
			}
		}

		private void DownLoadError(string message) {
			Response.ClearContent();
			//Response.Write(Fail);
			//// ReSharper disable AssignNullToNotNullAttribute
			//Response.Write(ex.GetType().FullName);
			//// ReSharper restore AssignNullToNotNullAttribute
			//Response.Write(": ");
			Response.Write(message);
			Response.Flush();
			Response.End();
		}

		protected virtual void OnCustomCallback(CustomCallbackEventArgs e) {
			var handler = (CustomCallbackEventHandler)Events[CustomCallbackEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}
		/// <summary>
		/// Always return true
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]

		public override bool ClientEnable {
			get {
				return true;
			}
			set {
				//base.ClientEnable = value;
			}
		}
		/// <summary>
		/// Alway return false
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public override bool ClientVisible {
			get { return false; }
			set {
			}
		}

		public event CustomCallbackEventHandler CustomCallback {
			add {
				Events.AddHandler(CustomCallbackEvent, value);
			}
			remove {
				Events.RemoveHandler(CustomCallbackEvent, value);
			}
		}

		public event EventHandler<CustomDownloadEventArgs> CustomDownload {
			add {
				Events.AddHandler(CustomDownloadEvent, value);
			}
			remove {
				Events.RemoveHandler(CustomDownloadEvent, value);
			}
		}
	}
}
